package com.director.core;

import com.director.core.annotation.DirectParam;
import org.apache.commons.lang.StringUtils;

/**
 * @author Simone Ricciardi
 * @version 1.0, 10/23/2011
 */
public class DirectMethodParam {

   private String name;
   private int order;
   private Class<Object> type;
   private boolean required;
   private String factoryName;
   private ParameterStrategy parameterStrategy = new OrderParameterStrategy();

   public DirectMethodParam(int order, Class<Object> type, DirectParam directParam) {
      this.order = order;
      this.type = type;
      if(directParam != null) {
         this.name = directParam.name();
         this.required = directParam.required();
         this.factoryName = directParam.factory();
         if(StringUtils.isNotBlank(this.name)) {
            this.parameterStrategy = new NamedParameterStrategy();
         }
      }
   }

   public Object parseFromTransactionData(DirectTransactionData data) {
      if(StringUtils.isNotBlank(this.factoryName)) {
         ParameterFactory factory = DirectContext.get().getConfiguration().getParameterFactory(factoryName);
         return factory.create(type, this.parameterStrategy.parse(data, factory.getInputType()));
      } else {
         return this.parameterStrategy.parse(data, type);
      }
   }

   public Object getDefaultValue() {
      return (this.type.isPrimitive()) ? PrimitiveDefaults.getDefaultValue(this.type) : null;
   }

   interface ParameterStrategy {
      Object parse(DirectTransactionData data, Class<Object> type);
   }

   class OrderParameterStrategy implements ParameterStrategy {

      @Override
      public Object parse(DirectTransactionData data, Class<Object> type) {
         return data.parseValue(order, type);
      }
   }

   class NamedParameterStrategy implements ParameterStrategy {

      @Override
      public Object parse(DirectTransactionData data, Class<Object> type) {
         return data.parseValue(name, type);
      }
   }

   public static class PrimitiveDefaults {
      private static boolean DEFAULT_BOOLEAN;
      private static byte DEFAULT_BYTE;
      private static short DEFAULT_SHORT;
      private static int DEFAULT_INT;
      private static long DEFAULT_LONG;
      private static float DEFAULT_FLOAT;
      private static double DEFAULT_DOUBLE;

      public static Object getDefaultValue(Class clazz) {
         if(clazz.equals(boolean.class)) {
            return DEFAULT_BOOLEAN;
         } else if(clazz.equals(byte.class)) {
            return DEFAULT_BYTE;
         } else if(clazz.equals(short.class)) {
            return DEFAULT_SHORT;
         } else if(clazz.equals(int.class)) {
            return DEFAULT_INT;
         } else if(clazz.equals(long.class)) {
            return DEFAULT_LONG;
         } else if(clazz.equals(float.class)) {
            return DEFAULT_FLOAT;
         } else if(clazz.equals(double.class)) {
            return DEFAULT_DOUBLE;
         } else {
            throw new IllegalArgumentException("Class type " + clazz + " not supported");
         }
      }
   }
}
